package echiquier;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import piece.Cavalier;
import piece.Fou;
import piece.Piece;
import piece.PieceType;
import piece.Pion;
import piece.Reine;
import piece.Roi;
import piece.Tour;
import core.AjoutPieceException;
import core.Coordonnees;
import core.Couleur;
import core.MutationException;
import core.PlacementException;

/**
 * Classe qui représente l'ensemble des pièces d'un des deux joueurs. Un jeu est soit de Couleur Noir, soit de Couleur Blanche.
 * @author baptiste.andrini
 *
 */
public class Jeu {
	private HashSet<Piece> pieces;
	private Couleur couleur;
	
	/**
	 * Constructeur, construit le jeu à partir d'une couleur et d'un plateau
	 * @param couleur
	 * @param plateau
	 */
	public Jeu(Couleur couleur, HashMap<Coordonnees, Case> plateau) {
		this.couleur = couleur;
		this.construireJeu(plateau);
	}

	/**
	 * Accesseur de la couleur du jeu
	 * @return
	 */
	public Couleur getCouleur() {
		return couleur;
	}
	
	/**
	 * Accesseur des pièces valides
	 * @return
	 */
	public int getNbPiecesValides() {
		int i = 0;
		
		for (Piece p : pieces) {
			if (p != null && !p.estElimine()) {
				i++;
			}
		}
		return i;
	}
	
	/**
	 * Accesseur du nombre de pièce d'un type donné
	 * @param type
	 * @return
	 */
	public int getNbTypePiece(PieceType type) {
		int i = 0;
		
		for (Piece p : pieces) {
			if (p.getType() == type) {
				i++;
			}
		}
		return i;
	}
	
	/**
	 * Accesseur de la collection de pieces
	 * @return
	 */
	public HashSet<Piece> getPieces() {
		return this.pieces;
	}
	
	/**
	 * Accesseur du roi
	 * @return
	 * @throws RoiNonTrouvableException
	 */
	public Piece getRoi() {
		Piece roi = null;
		
		Iterator<Piece> i = pieces.iterator();
		while (i.hasNext() && (roi == null || roi.getType() != PieceType.Roi)) {
			roi = i.next();
		}
		
		return roi;
	}
	
	/**
	 * Test de la bonne initialisation du jeu
	 * @return
	 */
	public boolean testInitialisation() {
		int i = 0;
		
		for (Piece p : pieces) {
			if (p != null) {
				i++;
			}
		}
		return i == 16;
	}
	
	/**
	 * Ajoute une pièce d'un type passé en paramètre dans la collection de pièce de ce jeu.
	 * @param casePiece La case où la pièce est créé. Ajouté dans l'objet Piece, qui doit connaitre sa case actuelle.
	 * @param type Le type de la pièce à créer.
	 * @throws PlacementException 
	 * @throws AjoutPieceException 
	 * @throws Exception
	 */
	public Piece ajoutPiece(Case casePiece, PieceType type) throws PlacementException, AjoutPieceException {
		if (casePiece != null && casePiece.isFree()) {
			Piece p;
			if (type == PieceType.Cavalier)
				p = new Cavalier(casePiece, this);
			else if (type == PieceType.Roi && getNbTypePiece(type) == 0)
				p = new Roi(casePiece, this);
			else if (type == PieceType.Reine)
				p = new Reine(casePiece, this);
			else if (type == PieceType.Fou)
				p = new Fou(casePiece, this);
			else if (type == PieceType.Tour)
				p = new Tour(casePiece, this);
			else
				p = new Pion(casePiece, this);
			
			this.pieces.add(p);
			return p;
		} else {
			throw new AjoutPieceException();
		}
	}

	/**
	 * Cette méthode construit un nouveau jeu de pièce complète et les place sur l'échiqier passé en paramètre.
	 * @param plateau L'échiquié pour cette partie.
	 */
	private void construireJeu(HashMap<Coordonnees, Case> plateau){
		this.pieces = new HashSet<Piece>();
		int ytete, ypion;

		try {
			if(this.couleur == Couleur.Blanc){
				ytete = 1;
				ypion = 2;
			}else{
				ytete = 8;
				ypion = 7;
			}
			Coordonnees c = new Coordonnees(1, ytete);
			Coordonnees c2 = new Coordonnees(1, ypion);
			
			for(int x =1 ; x <= 8 ; x++){
				c.x = x;
				c2.x = x;
				System.out.println("Tentative d'ajout en " + c + " et en " + c2);
				
				ajoutPiece(plateau.get(c2), PieceType.Pion);
				
				if(c.x == 1 || c.x == 8)
					ajoutPiece(plateau.get(c), PieceType.Tour);
				else if(c.x == 2 || c.x == 7)
					ajoutPiece(plateau.get(c), PieceType.Cavalier);
				else if(c.x == 3 || c.x == 6)
					ajoutPiece(plateau.get(c), PieceType.Fou);
				else if(c.x == 4)
					ajoutPiece(plateau.get(c), PieceType.Reine);
				else if(c.x == 5)
					ajoutPiece(plateau.get(c), PieceType.Roi);
			}
		} catch (Exception e) {
			System.out.println("Problème lors de la création d'un nouveau jeu.");
			e.printStackTrace();
		}
	}

	/**
	 * Méthode pour muter un pion donné au type donné
	 * @param pion
	 * @param type
	 * @throws MutationException
	 * @throws PlacementException
	 * @throws AjoutPieceException
	 */
	public Piece pionMute(Pion pion, PieceType type) throws MutationException {
		Case caseMutation = pion.getCase();
		this.pieces.remove(pion);
		try {
			pion.finalize();
		}catch(Throwable t) {
			throw new MutationException();
		}
		try {
			return ajoutPiece(caseMutation, type);
		} catch (Exception e) {
			throw new MutationException();
		}
	}

	/**
	 * Fonction toString pour affichage
	 */
	public String toString() {
		String res;
		res = "Jeu " + this.couleur + " :\n";
		for (Piece p : pieces) {
			res += p+"\n";
		}
		return res;
	}
}
